﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace RRRSRoguelikeData
{
	/// <summary>
	/// A class for reading text files.
	/// </summary>
	public static class Files
	{
		/// <summary>
		/// A method that returns a string containing the contents of the file.
		/// </summary>
		/// <param name="filePath"></param>
		/// <returns>A string containing the contents of the file. </returns>
		public static string TxtFileToString(string filePath)
		{
			string text;
			//needs to be more robust.  Error handling etc.
			try
			{
				var streamReader = new StreamReader(filePath);
				text = streamReader.ReadToEnd();
				streamReader.Close();
			}
			catch
			{
				text = Path.GetFileName(filePath) + " not available.";
			}

			return text;
		}

		

		public static void WriteTileDataToXML(string filepath,TileData tileData)
		{
			var XmlDoc = new XmlDocument();
			
			XmlDeclaration xmlDeclaration = XmlDoc.CreateXmlDeclaration("1.0","utf-8",null); 			
			
			XmlNode root = XmlDoc.AppendChild(XmlDoc.CreateElement("TileData"));
			XmlDoc.InsertBefore(xmlDeclaration, XmlDoc.DocumentElement); 
			string ns = "http://www.w3.org/2001/XMLSchema-instance";
			XmlAttribute xmlns = XmlDoc.CreateAttribute("xmlns","xsi","http://www.w3.org/2000/xmlns/");                                       
			xmlns.Value = ns;
			root.Attributes.Append(xmlns);
			
			string SchemaImporter = "../TileData.xsd";
			XmlAttribute xsi = XmlDoc.CreateAttribute("xsi", "noNamespaceSchemaLocation", "http://www.w3.org/2001/XMLSchema-instance");
			xsi.Value = SchemaImporter;
			root.Attributes.Append(xsi);
			
			XmlNode name = root.AppendChild(XmlDoc.CreateElement("Name"));
			name.InnerText = tileData.Name;
    		
    		XmlNode imageCharacter = root.AppendChild(XmlDoc.CreateElement("ImageCharacter"));
			imageCharacter.InnerText = tileData.ImageCharacter;
			
			XmlNode description = root.AppendChild(XmlDoc.CreateElement("Description"));
			description.InnerText = tileData.Description;
			
			XmlNode forecolor = root.AppendChild(XmlDoc.CreateElement("ForeColor"));
			forecolor.InnerText = tileData.ForeColor;
			
			XmlNode backcolor = root.AppendChild(XmlDoc.CreateElement("BackColor"));
			backcolor.InnerText = tileData.BackColor;
			
			
			
			XmlNode walkable = root.AppendChild(XmlDoc.CreateElement("Walkable"));
			walkable.InnerText = tileData.Walkable.ToString().ToLower();
			
			XmlDoc.Save(filepath);


		}

		public static TileData ReadFromXML(string filepath)
		{
			var tileData = new TileData();
			var TileSerializer = new XmlSerializer(typeof(TileData));
			var TileFileStream = new FileStream(filepath, FileMode.Open, FileAccess.Read);
			try
			{


				tileData = (TileData)TileSerializer.Deserialize(TileFileStream);
			}
			catch
			{
				tileData.ForeColor = "Red";
				tileData.BackColor = "White";
				tileData.ImageCharacter = "?";
				tileData.Name = "missing file";
				tileData.Description = "missing file: " + filepath;
				tileData.Walkable = true;
				
			}
			
			TileFileStream.Close();
			return tileData;

		}

		public static void WriteDataToXML(string filepath, DungeonData data)
		{
			var XmlDoc = new XmlDocument();
			
			XmlDeclaration xmlDeclaration = XmlDoc.CreateXmlDeclaration("1.0","utf-8",null); 			
			
			XmlNode root = XmlDoc.AppendChild(XmlDoc.CreateElement("DungeonData"));
			XmlDoc.InsertBefore(xmlDeclaration, XmlDoc.DocumentElement); 
			string ns = "http://www.w3.org/2001/XMLSchema-instance";
			XmlAttribute xmlns = XmlDoc.CreateAttribute("xmlns","xsi","http://www.w3.org/2000/xmlns/");                                       
			xmlns.Value = ns;
			root.Attributes.Append(xmlns);
			
			string SchemaImporter = "../DungeonData.xsd";
			XmlAttribute xsi = XmlDoc.CreateAttribute("xsi", "noNamespaceSchemaLocation", "http://www.w3.org/2001/XMLSchema-instance");
			xsi.Value = SchemaImporter;
			root.Attributes.Append(xsi);
			
			XmlNode name = root.AppendChild(XmlDoc.CreateElement("Name"));
			name.InnerText = data.Name;
    		
    		XmlNode description = root.AppendChild(XmlDoc.CreateElement("Description"));
			description.InnerText = data.Description;
			
			XmlNode sounds = root.AppendChild(XmlDoc.CreateElement("Sounds"));		
			foreach (KeyValuePair<string,string> kvp in data.Sounds)
			{
			XmlNode sound = sounds.AppendChild(XmlDoc.CreateElement("Sound"));
			sound.InnerText = Path.Combine("Sound", kvp.Key + ".wav");
			}
    		
    		XmlNode messages = root.AppendChild(XmlDoc.CreateElement("Messages"));		
			foreach (KeyValuePair<string,string> kvp in data.Texts)
			{
			XmlNode message = messages.AppendChild(XmlDoc.CreateElement("Message"));
			message.InnerText = Path.Combine("Texts",kvp.Key + ".txt");
			}
    		
    		XmlNode players = root.AppendChild(XmlDoc.CreateElement("Players"));		
			foreach (KeyValuePair<string,TileData> kvp in data.Players)
			{
			XmlNode player = players.AppendChild(XmlDoc.CreateElement("Player"));
			player.InnerText = Path.Combine("Creatures",kvp.Key + ".xml");
			
			WriteTileDataToXML(Path.Combine(Path.GetDirectoryName(filepath),"Creatures",kvp.Key + ".xml"),kvp.Value);
			}
			
			XmlNode creatures = root.AppendChild(XmlDoc.CreateElement("Creatures"));		
			foreach (KeyValuePair<string,TileData> kvp in data.Creatures)
			{
			XmlNode creature = creatures.AppendChild(XmlDoc.CreateElement("Creature"));
			creature.InnerText = Path.Combine("Creatures",kvp.Key + ".xml");
			
			WriteTileDataToXML(Path.Combine(Path.GetDirectoryName(filepath),"Creatures",kvp.Key + ".xml"),kvp.Value);
			}
			
			XmlNode props = root.AppendChild(XmlDoc.CreateElement("Props"));		
			foreach (KeyValuePair<string,TileData> kvp in data.Props)
			{
			XmlNode prop = props.AppendChild(XmlDoc.CreateElement("Prop"));
			prop.InnerText = Path.Combine("Props",kvp.Key + ".xml");
			}
			
			XmlNode tiles = root.AppendChild(XmlDoc.CreateElement("Tiles"));		
			foreach (KeyValuePair<string,TileData> kvp in data.Tiles)
			{
			XmlNode tile = tiles.AppendChild(XmlDoc.CreateElement("Tile"));
			tile.InnerText = Path.Combine("Tiles",kvp.Key + ".xml");
			}
			
			XmlNode levels = root.AppendChild(XmlDoc.CreateElement("Levels"));		
			foreach (string s in data.Levels)
			{
			XmlNode level = levels.AppendChild(XmlDoc.CreateElement("Level"));
			level.InnerText = s;
			}
			
    		XmlDoc.Save(filepath);
    		
		}
		
		public static DungeonData ReadDataFromXML(string filepath)
		{
			var Data = new DungeonData();
			var XmlDoc = new XmlDocument();
			

			XmlDoc.Load(filepath);

			var Name = XmlDoc.GetElementsByTagName("Name");
			var Desription = XmlDoc.GetElementsByTagName("Description");
			var Sounds = XmlDoc.GetElementsByTagName("Sound");
			var Messages = XmlDoc.GetElementsByTagName("Message");
			var Players = XmlDoc.GetElementsByTagName("Player");
			var Creatures = XmlDoc.GetElementsByTagName("Creature");
			var Props = XmlDoc.GetElementsByTagName("Prop");
			var Tiles = XmlDoc.GetElementsByTagName("Tile");
			var Levels = XmlDoc.GetElementsByTagName("Level");

			Data.Name = Name[0].InnerText;
			Data.Description = Desription[0].InnerText;

			//this should load the txt from file
			foreach (XmlNode n in Messages)
				Data.Texts.Add(Path.GetFileNameWithoutExtension(n.InnerText),
					Files.TxtFileToString(Path.Combine(Path.GetDirectoryName(filepath), @n.InnerText)));

			foreach (XmlNode n in Sounds)
				Data.Sounds.Add(Path.GetFileNameWithoutExtension(n.InnerText),
					Path.Combine(Path.GetDirectoryName(filepath), @n.InnerText));
			
			foreach (XmlNode n in Players)
				Data.Players.Add(Path.GetFileNameWithoutExtension(n.InnerText),
					Files.ReadFromXML(Path.Combine(Path.GetDirectoryName(filepath), n.InnerText)));

			foreach (XmlNode n in Creatures)
				Data.Creatures.Add(Path.GetFileNameWithoutExtension(n.InnerText),
					Files.ReadFromXML(Path.Combine(Path.GetDirectoryName(filepath), n.InnerText)));

			foreach (XmlNode n in Props)
				Data.Props.Add(Path.GetFileNameWithoutExtension(n.InnerText),
					Files.ReadFromXML(Path.Combine(Path.GetDirectoryName(filepath), n.InnerText)));

			foreach (XmlNode n in Tiles)
				Data.Tiles.Add(Path.GetFileNameWithoutExtension(n.InnerText),
					Files.ReadFromXML(Path.Combine(Path.GetDirectoryName(filepath), n.InnerText)));

			foreach (XmlNode n in Levels)
				Data.Levels.Add(n.InnerText);


			return Data;

		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="sourcePath"></param>
		/// <param name="targetPath"></param>
		/// <param name="overwrite"></param>
		public static void CopyFolder(string sourcePath, string targetPath, bool overwrite)
		{
			if (Directory.Exists(sourcePath))
			{
				var files = Directory.GetFiles(sourcePath);

				// Copy the files and overwrite destination files if they already exist. 
				foreach (var s in files)
				{
					// Use static Path methods to extract only the file name from the path.
					var fileName = Path.GetFileName(s);
					var destFile = Path.Combine(targetPath, fileName);

					//Create targetPath
					Directory.CreateDirectory(targetPath);

					if (overwrite || !File.Exists(destFile))
					{
						File.Copy(s, destFile, overwrite);
					}

				}
				var folders = Directory.GetDirectories(sourcePath);
				foreach (var s in folders)
				{
					var name = Path.GetFileName(s);
					var dest = Path.Combine(targetPath, name);
					CopyFolder(s, dest, false);
				}
			}
			else
			{
				//FIXME should be a localized string 
				Console.WriteLine("Source path does not exist!");
				Console.ReadKey();
			}
		}

	}
}
